#include "perf_monitor.h"
#include "../stats/statistics_collector.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <pthread.h>
#include <math.h>

// Global performance monitor
perf_monitor_t* g_perf_monitor = NULL;

// Utility function to get current time in microseconds
static uint64_t get_time_microseconds(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}

// Generate query hash (simple hash for identification)
static void generate_query_hash(const char* query, char* hash_out) {
    uint64_t hash = 0;
    const char* p = query;
    
    while (*p) {
        hash = hash * 31 + *p;
        p++;
    }
    
    snprintf(hash_out, 65, "%016llx", hash);
}

// Create performance monitor
perf_monitor_t* perf_monitor_create(const perf_monitor_config_t* config) {
    if (!config) {
        return NULL;
    }
    
    perf_monitor_t* monitor = calloc(1, sizeof(perf_monitor_t));
    if (!monitor) {
        return NULL;
    }
    
    // Copy configuration
    memcpy(&monitor->config, config, sizeof(perf_monitor_config_t));
    
    // Initialize mutex
    if (pthread_mutex_init(&monitor->mutex, NULL) != 0) {
        free(monitor);
        return NULL;
    }
    
    monitor->start_time = time(NULL);
    monitor->last_metrics_update = monitor->start_time;
    
    return monitor;
}

// Initialize performance monitor
int perf_monitor_init(perf_monitor_t* monitor, logger_t* logger, 
                     statistics_collector_t* stats_collector) {
    if (!monitor || monitor->initialized) {
        return -1;
    }
    
    monitor->logger = logger;
    monitor->stats_collector = stats_collector;
    monitor->initialized = true;
    
    // Log initialization
    if (logger) {
        LOG_INFO("PERF_MONITOR", 
                 "Performance monitor initialized: slow_threshold=%llu ms, "
                 "query_logging=%s, metrics_interval=%u ms",
                 monitor->config.slow_query_threshold_ms,
                 monitor->config.enable_query_logging ? "enabled" : "disabled",
                 monitor->config.metrics_collection_interval_ms);
    }
    
    return 0;
}

// Destroy performance monitor
void perf_monitor_destroy(perf_monitor_t* monitor) {
    if (!monitor) {
        return;
    }
    
    if (monitor->initialized && monitor->logger) {
        LOG_INFO("PERF_MONITOR", 
                 "Performance monitor shutting down: total_queries=%llu, "
                 "slow_queries=%llu, errors=%llu",
                 monitor->query_counter,
                 monitor->slow_query_counter,
                 monitor->error_counter);
    }
    
    pthread_mutex_destroy(&monitor->mutex);
    free(monitor);
    
    if (g_perf_monitor == monitor) {
        g_perf_monitor = NULL;
    }
}

// Set global performance monitor
int perf_monitor_set_global(perf_monitor_t* monitor) {
    g_perf_monitor = monitor;
    return 0;
}

// Get global performance monitor
perf_monitor_t* perf_monitor_get_global(void) {
    return g_perf_monitor;
}

// Start query monitoring
query_execution_context_t* perf_monitor_start_query(perf_monitor_t* monitor, 
                                                   const char* query_text,
                                                   const char* username,
                                                   const char* database_name) {
    if (!monitor || !monitor->initialized || !query_text) {
        return NULL;
    }
    
    query_execution_context_t* context = calloc(1, sizeof(query_execution_context_t));
    if (!context) {
        return NULL;
    }
    
    // Record start time
    context->start_time_us = get_time_microseconds();
    
    // Copy query text (truncate if necessary)
    strncpy(context->query_text, query_text, sizeof(context->query_text) - 1);
    context->query_text[sizeof(context->query_text) - 1] = '\0';
    
    // Generate query hash
    generate_query_hash(query_text, context->query_hash);
    
    // Set user and database context
    context->username = username;
    context->database_name = database_name;
    
    return context;
}

// End query monitoring
void perf_monitor_end_query(perf_monitor_t* monitor, 
                           query_execution_context_t* context,
                           uint64_t rows_examined,
                           uint64_t rows_returned,
                           uint64_t bytes_sent,
                           const char* error_message) {
    if (!monitor || !monitor->initialized || !context) {
        return;
    }
    
    // Record end time and calculate execution time
    context->end_time_us = get_time_microseconds();
    uint64_t execution_time_ms = (context->end_time_us - context->start_time_us) / 1000;
    
    // Update context
    context->rows_examined = rows_examined;
    context->rows_returned = rows_returned;
    context->bytes_sent = bytes_sent;
    
    if (error_message) {
        context->has_error = true;
        strncpy(context->error_message, error_message, sizeof(context->error_message) - 1);
        context->error_message[sizeof(context->error_message) - 1] = '\0';
    }
    
    pthread_mutex_lock(&monitor->mutex);
    
    // Update counters
    monitor->query_counter++;
    if (context->has_error) {
        monitor->error_counter++;
    }
    
    // Update execution time statistics
    monitor->total_execution_time_ms += execution_time_ms;
    if (execution_time_ms > monitor->max_execution_time_ms) {
        monitor->max_execution_time_ms = execution_time_ms;
    }
    
    // Check if this is a slow query
    bool is_slow = perf_monitor_is_slow_query(monitor, execution_time_ms);
    if (is_slow) {
        monitor->slow_query_counter++;
    }
    
    pthread_mutex_unlock(&monitor->mutex);
    
    // Log query execution
    if (monitor->logger) {
        if (context->has_error) {
            LOG_ERROR("QUERY", "FAILED [%llu ms] [%llu/%llu rows] [%s] User: %s, Query: %.100s",
                     execution_time_ms, rows_returned, rows_examined,
                     context->error_message,
                     context->username ? context->username : "unknown",
                     context->query_text);
        } else if (is_slow && monitor->config.enable_slow_query_logging) {
            LOG_WARN("SLOW_QUERY", "DETECTED [%llu ms] [%llu/%llu rows] [%llu bytes] User: %s, Query: %.200s",
                    execution_time_ms, rows_returned, rows_examined, bytes_sent,
                    context->username ? context->username : "unknown",
                    context->query_text);
        } else if (monitor->config.enable_query_logging) {
            LOG_INFO("QUERY", "SUCCESS [%llu ms] [%llu/%llu rows] [%llu bytes] User: %s, Query: %.100s",
                    execution_time_ms, rows_returned, rows_examined, bytes_sent,
                    context->username ? context->username : "unknown",
                    context->query_text);
        }
    }
    
    // Update statistics collector if available
    if (monitor->stats_collector) {
        // Record query statistics
        stats_record_query(monitor->stats_collector, 
                          context->query_hash,
                          execution_time_ms,
                          rows_examined,
                          rows_returned);
    }
}

// Free query context
void perf_monitor_free_query_context(query_execution_context_t* context) {
    if (context) {
        free(context);
    }
}

// Check if query is slow
bool perf_monitor_is_slow_query(perf_monitor_t* monitor, uint64_t execution_time_ms) {
    if (!monitor) {
        return false;
    }
    return execution_time_ms >= monitor->config.slow_query_threshold_ms;
}

// Update system metrics
int perf_monitor_update_metrics(perf_monitor_t* monitor) {
    if (!monitor || !monitor->initialized) {
        return -1;
    }
    
    time_t now = time(NULL);
    
    pthread_mutex_lock(&monitor->mutex);
    
    // Calculate time since last update
    time_t elapsed = now - monitor->last_metrics_update;
    if (elapsed == 0) elapsed = 1; // Avoid division by zero
    
    // Update system performance metrics
    monitor->metrics.total_queries_count = monitor->query_counter;
    monitor->metrics.slow_queries_count = monitor->slow_query_counter;
    monitor->metrics.failed_queries_count = monitor->error_counter;
    
    // Calculate QPS (queries in last interval)
    static uint64_t last_query_count = 0;
    uint64_t queries_in_interval = monitor->query_counter - last_query_count;
    monitor->metrics.queries_per_second = queries_in_interval / elapsed;
    last_query_count = monitor->query_counter;
    
    // Calculate average query time
    if (monitor->query_counter > 0) {
        monitor->metrics.avg_query_time_ms = monitor->total_execution_time_ms / monitor->query_counter;
    }
    monitor->metrics.max_query_time_ms = monitor->max_execution_time_ms;
    
    monitor->metrics.last_update_time = now;
    monitor->last_metrics_update = now;
    
    pthread_mutex_unlock(&monitor->mutex);
    
    return 0;
}

// Get system metrics
int perf_monitor_get_metrics(perf_monitor_t* monitor, 
                            system_performance_metrics_t* metrics) {
    if (!monitor || !metrics) {
        return -1;
    }
    
    pthread_mutex_lock(&monitor->mutex);
    memcpy(metrics, &monitor->metrics, sizeof(system_performance_metrics_t));
    pthread_mutex_unlock(&monitor->mutex);
    
    return 0;
}

// Log system status
void perf_monitor_log_system_status(perf_monitor_t* monitor) {
    if (!monitor || !monitor->logger) {
        return;
    }
    
    perf_monitor_update_metrics(monitor);
    
    system_performance_metrics_t metrics;
    if (perf_monitor_get_metrics(monitor, &metrics) == 0) {
        LOG_INFO("SYSTEM_STATUS", 
                 "QPS: %llu, Total Queries: %llu, Slow Queries: %llu (%.1f%%), "
                 "Failed: %llu (%.1f%%), Avg Time: %.2f ms, Max Time: %.2f ms",
                 metrics.queries_per_second,
                 metrics.total_queries_count,
                 metrics.slow_queries_count,
                 metrics.total_queries_count > 0 ? 
                   (double)metrics.slow_queries_count * 100.0 / metrics.total_queries_count : 0.0,
                 metrics.failed_queries_count,
                 metrics.total_queries_count > 0 ? 
                   (double)metrics.failed_queries_count * 100.0 / metrics.total_queries_count : 0.0,
                 metrics.avg_query_time_ms,
                 metrics.max_query_time_ms);
    }
}

// Log slow query summary
void perf_monitor_log_slow_query_summary(perf_monitor_t* monitor) {
    if (!monitor || !monitor->logger) {
        return;
    }
    
    pthread_mutex_lock(&monitor->mutex);
    
    if (monitor->slow_query_counter > 0) {
        double slow_query_rate = monitor->query_counter > 0 ? 
            (double)monitor->slow_query_counter * 100.0 / monitor->query_counter : 0.0;
        
        LOG_WARN("SLOW_QUERY_SUMMARY", 
                 "Slow queries detected: %llu out of %llu total (%.2f%%), "
                 "threshold: %llu ms",
                 monitor->slow_query_counter,
                 monitor->query_counter,
                 slow_query_rate,
                 monitor->config.slow_query_threshold_ms);
    }
    
    pthread_mutex_unlock(&monitor->mutex);
}

// Connection tracking
void perf_monitor_connection_opened(perf_monitor_t* monitor) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    monitor->metrics.active_connections++;
    monitor->metrics.total_connections++;
    pthread_mutex_unlock(&monitor->mutex);
}

void perf_monitor_connection_closed(perf_monitor_t* monitor) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    if (monitor->metrics.active_connections > 0) {
        monitor->metrics.active_connections--;
    }
    pthread_mutex_unlock(&monitor->mutex);
}

// Cache statistics
void perf_monitor_cache_hit(perf_monitor_t* monitor) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    // Update cache hit ratio (simplified implementation)
    static uint64_t cache_hits = 0;
    static uint64_t cache_total = 0;
    
    cache_hits++;
    cache_total++;
    
    monitor->metrics.cache_hit_ratio_percent = cache_total > 0 ? 
        (cache_hits * 100) / cache_total : 0;
    
    pthread_mutex_unlock(&monitor->mutex);
}

void perf_monitor_cache_miss(perf_monitor_t* monitor) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    // Update cache hit ratio (simplified implementation)
    static uint64_t cache_hits = 0;
    static uint64_t cache_total = 0;
    
    cache_total++;
    
    monitor->metrics.cache_hit_ratio_percent = cache_total > 0 ? 
        (cache_hits * 100) / cache_total : 0;
    
    pthread_mutex_unlock(&monitor->mutex);
}

// Disk I/O tracking
void perf_monitor_disk_read(perf_monitor_t* monitor, uint64_t bytes) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    static uint64_t total_read_bytes = 0;
    static time_t last_read_time = 0;
    
    total_read_bytes += bytes;
    time_t now = time(NULL);
    
    if (now != last_read_time && last_read_time != 0) {
        monitor->metrics.disk_reads_per_second = total_read_bytes / (now - last_read_time);
        total_read_bytes = 0;
        last_read_time = now;
    } else if (last_read_time == 0) {
        last_read_time = now;
    }
    
    pthread_mutex_unlock(&monitor->mutex);
}

void perf_monitor_disk_write(perf_monitor_t* monitor, uint64_t bytes) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    static uint64_t total_write_bytes = 0;
    static time_t last_write_time = 0;
    
    total_write_bytes += bytes;
    time_t now = time(NULL);
    
    if (now != last_write_time && last_write_time != 0) {
        monitor->metrics.disk_writes_per_second = total_write_bytes / (now - last_write_time);
        total_write_bytes = 0;
        last_write_time = now;
    } else if (last_write_time == 0) {
        last_write_time = now;
    }
    
    pthread_mutex_unlock(&monitor->mutex);
}

// Memory usage tracking
void perf_monitor_update_memory_usage(perf_monitor_t* monitor, uint64_t memory_mb) {
    if (!monitor) return;
    
    pthread_mutex_lock(&monitor->mutex);
    monitor->metrics.memory_usage_mb = memory_mb;
    pthread_mutex_unlock(&monitor->mutex);
}